/* Object的扩展 */
const { log } = console
// 1. 对象的取值器和赋值器
let f1 = () => {
  let car = {
    // 对象的属性
    _wheel: 3,

    // getter
    get wheel() {
      log('getter')
      return this._wheel
    },

    // setter
    set wheel(v) {
      log('setter')
      if (typeof v !== 'number' || v <= 0 || !Number.isInteger(v)) {
        throw '车子的轮子必须是大于0的整数'
      }
      this._wheel = v
    },
  }
  car.wheel = 4
  log(car._wheel) // 3
}

// 2. 对象属性的描述器
let f2 = () => {
  let stu = {
    id: 1,
    age: 20,
    name: 'tom',
    gender: '男',
    info() {
      log(this)
    },
  }
  // 获得对象stu属性name的描述器
  let descriptor = Object.getOwnPropertyDescriptor(stu, 'name')
  // console.log(descriptor)
  /*
    {
      value: 'tom', // name的值是tom
      writable: true, // name的值是可以修改的
      enumerable: true, // name属性可枚举
      configurable: true // name属性可以配置
    }
  */
  // 定义对象stu的name属性
  Object.defineProperty(stu, 'name', {
    value: 'tom', // name的值是tom
    writable: false, // name的值是不可以修改
    enumerable: false, // name属性不可枚举
    configurable: false, // name属性不可以配置
  })
  stu.name = 'wenddy'
  // log(stu.name)
  for (let prop in stu) {
    log(prop)
  }
}

// 定义一个对象
let stu = {
  id: 1,
  age: 20,
  name: 'tom',
  gender: '男',
  info() {
    log(this)
  },
}
// 3. 对象属性劫持
let f3 = () => {
  // 把对象的所有属性进行遍历
  let keys = Reflect.ownKeys(stu)
  keys.forEach((item) => {
    defineReactive(stu, item, stu[item])
  })
  // 监听input的输入事件
  let input = document.querySelector('.username')
  input.addEventListener(
    'input',
    function (e) {
      stu.name = this.value
    },
    false
  )
  // 执行观察操作
  oberseve()
}
// 对象的属性发生变化时就会调用set方法，可以在set方法中做监听操作
function defineReactive(obj, prop, value) {
  Object.defineProperty(obj, prop, {
    set(v) {
      // 赋值器
      // log('setter')
      // log(`对象的属性${prop}发生变化了,我们可以调用其他函数，实现一个功能`)
      if (v !== value) {
        value = v
        oberseve()
      }
    },
    get() {
      // 取值器
      // log('getter')
      return value
    },
  })
}
// 观察函数
function oberseve() {
  document.querySelector('.info').innerHTML = JSON.stringify(stu)
}

// 4. 对象属性的5种遍历方式
let f4 = () => {
  let stu = {
    id: 1,
    age: 20,
    name: 'tom',
    gender: '男',
    info() {
      log(this)
    },
    // 属性不可枚举
    [Symbol()]: 'hello',
    [Symbol()]: 'world',
  }
  log('-----------------for-in-------------------')
  // 4-1 for-in 遍历可枚举属性
  for (let key in stu) {
    log(stu[key])
  }
  log('-----------------Object.keys(stu)-------------------')
  // 4-2 Object.keys(stu) 遍历可枚举属性
  let keys = Object.keys(stu)
  for (let item of keys) {
    log(item)
  }
  log('-----------------Object.getOwnPropertyNames(stu)-------------------')
  // 4-3 Object.getOwnPropertyNames(stu) 遍历可枚举属性名称
  let props = Object.getOwnPropertyNames(stu)
  for (let item of props) {
    log(item)
  }
  log('-----------------Object.getOwnPropertySymbols(stu)-------------------')
  // 4-4 Object.getOwnPropertySymbols(stu) 遍历symbol属性名称
  let symbolProps = Object.getOwnPropertySymbols(stu)
  for (let item of symbolProps) {
    log(item, stu[item])
  }
  log('-----------------Reflect.ownKeys(stu)-------------------')
  // 4-5 Reflect.ownKeys(stu) 遍历所有是属性
  let reflectKeys = Reflect.ownKeys(stu)
  let i = 0
  for (let item of reflectKeys) {
    log(i, item)
    i++
  }
}
// f4()

// 5. 对象中super关键字，指向当前对象的原型对象
let f5 = () => {
  const proto = { foo: 'hello' }
  const obj = {
    foo: 'world',
    find() {
      return super.foo
    },
  }
  // 指定obj对象的原型proto
  Object.setPrototypeOf(obj, proto)
  let res = obj.find()
  log(res, obj.foo) // hello world
}
// f5()

// 6. 对象扩展运算符
let f6 = () => {
  let st1 = { a: 1, b: 2, c: 3 }
  let st2 = { a: 2, b: 3, d: 4, f: 5 }
  let st = { ...st1, ...st2 }
  log(st) // { a: 2, b: 3, c: 3, d: 4, f: 5 }
}

// 7. 链式判断运算符 ?.
let f7 = () => {
  /*
    obj?.prop // 对象属性
    obj?.[expr] // 同上
    func?.(...args) // 函数或对象方法的调用
  */
  let message = {
    body: {
      user: {
        firstName: '凯奇',
        lastName: '尼古拉斯',
      },
    },
    info() {
      log(this)
    },
  }
  // 避免空指针异常，进行链式操作判断
  const firstName = message?.body?.user?.firstName || 'default'
  log(firstName)
  // 对象函数的调用
  message?.info?.()
}
// f7()

// 8. Null 判断运算符 当值为null 或 undefined时
let f8 = () => {
  let response = {
    settings: {
      // headerText: false,
    },
  }
  // 问题是headerText如果是NaN false 0 '' 都会使用默认值
  const headerText = response.settings.headerText || 'Hello, world!'
  log(headerText)
  // 本意是headerText是null或undefined时，才会使用默认值
  const text = response.settings.headerText ?? 'Hello, world!'
  log(text)
}
// f8()

/*
function ajaxs(opt) {
  $.ajax({
    ...opt,
    success(res) {
      log(res)
    },
  })
}
ajaxs({
  url: 'http://146.56.230.35:3007/users/login',
  type: 'POST',
  data: { username: 'root123', userPassword: 'root123' },
})
*/

// 9. 对象新增方法Object.is(x,y) 严格相等运算符判断x和y是否相等NaN===NaN 返回true
let f9 = () => {
  let res = Object.is({}, {}) // false
  let res1 = Object.is([], []) // false
  let res2 = Object.is('str', 'str') // true
  let res3 = Object.is(NaN, NaN) // true
}

// 10. Object.assign(a,b,c) 把b,c的可枚举属性，全部合并给a
let f10 = () => {
  let obj = {}
  let rt = Object.assign(obj, { a: 1, b: 2 }, { c: { id: 3 }, list: [2, 3] })
  log(rt === obj) // true
  log(obj) //
}
// f10()

// 11. Object.setPrototypeOf(a,proto)指定对象a的原型为proto, Object.getPrototypeOf(a)获得对象a的原型
let f11 = () => {
  const proto = { foo: 'hello' }
  const obj = { foo: 'world' }
  Object.setPrototypeOf(obj, proto)
  let pt = Object.getPrototypeOf(obj)
  console.log(pt) // proto = {foo: 'hello'}
}
// f11()

// 12. Object.keys()，Object.values()，Object.entries()
let f12 = () => {
  let { keys, values, entries } = Object
  let obj = { a: 1, b: 2, c: 3 }
  log('-------------------Object.keys()---------------------')
  for (let key of keys(obj)) {
    log(key)
  }
  log('-------------------Object.values()---------------------')
  for (let value of values(obj)) {
    log(value)
  }
  log('-------------------Object.entries()---------------------')
  for (let [key, value] of entries(obj)) {
    log(key, value)
  }
}
f12()

// 13. Object.fromEntries([['id',1],['name','tom'],['age',20]])
let map = new Map([
  ['id', 1],
  ['name', 'tom'],
  ['age', 20],
])
let obj = Object.fromEntries(map) // {id: 1, name: 'tom', age: 20}
log(obj.name === map.get('name'))
